package service

import (
	"gitee.com/war2001/NiuMaEasyGo/model"
	"gitee.com/war2001/NiuMaEasyGo/nm_core/router"
	"go.uber.org/zap"
	"gorm.io/gorm"
)

// PermissionService 权限服务
type PermissionService struct {
	DB       *gorm.DB
	Registry *router.ControllerRegistry
}

// GetUserRoles 获取用户角色
func (s *PermissionService) GetUserRoles(userID uint64) ([]model.SysRole, error) {
	var roles []model.SysRole
	var roleIDs []uint64

	// 获取用户角色ID
	if err := s.DB.Table("sys_user_role").
		Where("user_id = ?", userID).
		Pluck("role_id", &roleIDs).Error; err != nil {
		return nil, err
	}

	// 获取角色详情
	if len(roleIDs) > 0 {
		if err := s.DB.Where("id IN ?", roleIDs).Find(&roles).Error; err != nil {
			return nil, err
		}
	}

	return roles, nil
}

// NewPermissionService 创建权限服务
func NewPermissionService(db *gorm.DB, registry *router.ControllerRegistry) *PermissionService {
	return &PermissionService{
		DB:       db,
		Registry: registry,
	}
}

// GetUserMenus 获取用户菜单
func (s *PermissionService) GetUserMenus(userID uint64, tenantID uint64) ([]*model.SysMenu, error) {
	var menus []*model.SysMenu

	zap.L().Info("开始获取用户菜单",
		zap.Uint64("userID", userID),
		zap.Uint64("tenantID", tenantID))

	// 1. 首先检查是否是admin用户（直接通过用户名判断）
	var user model.User
	var isAdminUser bool
	if err := s.DB.Where("id = ?", userID).First(&user).Error; err == nil {
		isAdminUser = user.Username == "admin"
		zap.L().Info("用户信息",
			zap.Uint64("userID", userID),
			zap.String("username", user.Username),
			zap.Bool("isAdminUser", isAdminUser))
	}

	// 2. 获取用户角色（作为备用检查）
	var roleIDs []uint64
	s.DB.Table("sys_user_role").
		Where("user_id = ?", userID).
		Pluck("role_id", &roleIDs)

	zap.L().Info("用户角色信息",
		zap.Uint64("userID", userID),
		zap.Any("roleIDs", roleIDs))

	// 3. 检查是否是系统管理员（admin用户或有admin角色）
	var isSysAdmin bool
	if isAdminUser {
		isSysAdmin = true
		zap.L().Info("通过用户名识别为系统管理员", zap.String("username", user.Username))
	} else if len(roleIDs) > 0 {
		var count int64
		s.DB.Model(&model.SysRole{}).
			Where("id IN ? AND code = ? AND tenant_id IS NULL", roleIDs, "admin").
			Count(&count)
		isSysAdmin = count > 0

		zap.L().Info("通过角色检查系统管理员",
			zap.Bool("isSysAdmin", isSysAdmin),
			zap.Int64("adminRoleCount", count))
	}

	// 4. 检查是否是租户管理员
	var isTenantAdmin bool
	if len(roleIDs) > 0 && tenantID > 0 {
		var count int64
		s.DB.Model(&model.SysRole{}).
			Where("id IN ? AND code = ? AND tenant_id = ?", roleIDs, "tenant_admin", tenantID).
			Count(&count)
		isTenantAdmin = count > 0
	}

	// 5. 根据不同角色获取菜单
	if isSysAdmin {
		// 系统管理员直接从控制器注册表获取所有菜单
		zap.L().Info("用户是系统管理员，直接从控制器获取所有菜单")

		if s.Registry != nil {
			// 从控制器注册表生成菜单
			controllerMenus := s.Registry.GenerateMenus()
			zap.L().Info("从控制器获取菜单", zap.Int("控制器菜单数", len(controllerMenus)))

			// 直接返回控制器生成的菜单，不需要查询数据库
			menus = controllerMenus
		} else {
			// 如果没有注册表，回退到数据库查询
			zap.L().Warn("控制器注册表为空，回退到数据库查询")
			result := s.DB.Where("type IN ? AND (menu_category IN ? OR menu_category IS NULL)",
				[]string{"directory", "menu"},
				[]string{"system", "common"}).
				Order("order_num").
				Find(&menus)

			if result.Error != nil {
				zap.L().Error("查询系统管理员菜单失败", zap.Error(result.Error))
			}
		}
	} else if isTenantAdmin && tenantID > 0 {
		// 租户管理员可以看到分配给该租户的所有菜单
		var tenantMenuIDs []uint64
		s.DB.Table("sys_tenant_menu").
			Where("tenant_id = ?", tenantID).
			Pluck("menu_id", &tenantMenuIDs)

		if len(tenantMenuIDs) > 0 {
			s.DB.Where("id IN ? AND type IN ?", tenantMenuIDs, []string{"directory", "menu"}).
				Order("order_num").
				Find(&menus)
		}
	} else if tenantID > 0 {
		// 普通租户用户，需要根据角色和租户过滤
		var menuIDs []uint64

		// 获取租户可用菜单
		var tenantMenuIDs []uint64
		s.DB.Table("sys_tenant_menu").
			Where("tenant_id = ?", tenantID).
			Pluck("menu_id", &tenantMenuIDs)

		// 获取角色菜单（必须是租户角色）
		var roleMenuIDs []uint64
		if len(roleIDs) > 0 {
			s.DB.Table("sys_role_menu rm").
				Joins("JOIN sys_role r ON rm.role_id = r.id").
				Where("rm.role_id IN ? AND r.tenant_id = ?", roleIDs, tenantID).
				Pluck("rm.menu_id", &roleMenuIDs)
		}

		// 取交集
		menuIDs = intersection(tenantMenuIDs, roleMenuIDs)

		// 5. 获取用户特殊授权的菜单
		var grantMenuIDs []uint64
		s.DB.Table("sys_user_menu").
			Where("user_id = ? AND type = ?", userID, "grant").
			Pluck("menu_id", &grantMenuIDs)

		// 合并菜单ID
		menuIDs = append(menuIDs, grantMenuIDs...)

		// 6. 获取用户特殊拒绝的菜单
		var denyMenuIDs []uint64
		s.DB.Table("sys_user_menu").
			Where("user_id = ? AND type = ?", userID, "deny").
			Pluck("menu_id", &denyMenuIDs)

		// 7. 过滤拒绝的菜单
		if len(denyMenuIDs) > 0 {
			var filteredMenuIDs []uint64
			for _, id := range menuIDs {
				denied := false
				for _, denyID := range denyMenuIDs {
					if id == denyID {
						denied = true
						break
					}
				}
				if !denied {
					filteredMenuIDs = append(filteredMenuIDs, id)
				}
			}
			menuIDs = filteredMenuIDs
		}

		if len(menuIDs) > 0 {
			s.DB.Where("id IN ? AND type IN ?", menuIDs, []string{"directory", "menu"}).
				Order("order_num").
				Find(&menus)
		}
	} else {
		// 普通用户，根据角色获取菜单
		if len(roleIDs) > 0 {
			var menuIDs []uint64
			s.DB.Table("sys_role_menu").
				Where("role_id IN ?", roleIDs).
				Pluck("menu_id", &menuIDs)

			// 获取用户特殊授权的菜单
			var grantMenuIDs []uint64
			s.DB.Table("sys_user_menu").
				Where("user_id = ? AND type = ?", userID, "grant").
				Pluck("menu_id", &grantMenuIDs)

			// 合并菜单ID
			menuIDs = append(menuIDs, grantMenuIDs...)

			// 获取用户特殊拒绝的菜单
			var denyMenuIDs []uint64
			s.DB.Table("sys_user_menu").
				Where("user_id = ? AND type = ?", userID, "deny").
				Pluck("menu_id", &denyMenuIDs)

			// 过滤拒绝的菜单
			if len(denyMenuIDs) > 0 {
				var filteredMenuIDs []uint64
				for _, id := range menuIDs {
					denied := false
					for _, denyID := range denyMenuIDs {
						if id == denyID {
							denied = true
							break
						}
					}
					if !denied {
						filteredMenuIDs = append(filteredMenuIDs, id)
					}
				}
				menuIDs = filteredMenuIDs
			}

			if len(menuIDs) > 0 {
				s.DB.Where("id IN ? AND type IN ? AND menu_category != ?",
					menuIDs,
					[]string{"directory", "menu"},
					"system").
					Order("order_num").
					Find(&menus)
			}
		}
	}

	// 构建菜单树
	finalMenus := s.buildMenuTree(menus)
	zap.L().Info("用户菜单获取完成",
		zap.Int("原始菜单数", len(menus)),
		zap.Int("最终菜单数", len(finalMenus)))
	return finalMenus, nil
}

// GetUserPermissions 获取用户权限
func (s *PermissionService) GetUserPermissions(userID uint64, tenantID uint64) ([]string, error) {
	var permissions []string

	// 1. 获取用户角色
	var roleIDs []uint64
	s.DB.Table("sys_user_role").
		Where("user_id = ?", userID).
		Pluck("role_id", &roleIDs)

	// 2. 检查是否是系统管理员
	var isSysAdmin bool
	if len(roleIDs) > 0 {
		var count int64
		s.DB.Model(&model.SysRole{}).
			Where("id IN ? AND code = ? AND tenant_id IS NULL", roleIDs, "admin").
			Count(&count)
		isSysAdmin = count > 0
	}

	// 3. 如果是系统管理员，返回所有权限
	if isSysAdmin {
		s.DB.Model(&model.SysMenu{}).
			Where("permission != ''").
			Pluck("permission", &permissions)
		return permissions, nil
	}

	// 4. 获取角色菜单
	var menuIDs []uint64
	if len(roleIDs) > 0 {
		if tenantID > 0 {
			// 租户用户，只能获取租户角色的权限
			s.DB.Table("sys_role_menu rm").
				Joins("JOIN sys_role r ON rm.role_id = r.id").
				Where("rm.role_id IN ? AND r.tenant_id = ?", roleIDs, tenantID).
				Pluck("rm.menu_id", &menuIDs)
		} else {
			// 普通用户，获取所有角色的权限
			s.DB.Table("sys_role_menu").
				Where("role_id IN ?", roleIDs).
				Pluck("menu_id", &menuIDs)
		}
	}

	// 5. 获取用户特殊授权的菜单
	var grantMenuIDs []uint64
	s.DB.Table("sys_user_menu").
		Where("user_id = ? AND type = ?", userID, "grant").
		Pluck("menu_id", &grantMenuIDs)

	// 合并菜单ID
	menuIDs = append(menuIDs, grantMenuIDs...)

	// 6. 获取用户特殊拒绝的菜单
	var denyMenuIDs []uint64
	s.DB.Table("sys_user_menu").
		Where("user_id = ? AND type = ?", userID, "deny").
		Pluck("menu_id", &denyMenuIDs)

	// 7. 过滤拒绝的菜单
	if len(denyMenuIDs) > 0 {
		var filteredMenuIDs []uint64
		for _, id := range menuIDs {
			denied := false
			for _, denyID := range denyMenuIDs {
				if id == denyID {
					denied = true
					break
				}
			}
			if !denied {
				filteredMenuIDs = append(filteredMenuIDs, id)
			}
		}
		menuIDs = filteredMenuIDs
	}

	// 8. 获取权限标识
	if len(menuIDs) > 0 {
		s.DB.Model(&model.SysMenu{}).
			Where("id IN ? AND permission != ''", menuIDs).
			Pluck("permission", &permissions)
	}

	return permissions, nil
}

// AssignRoleMenus 为角色分配菜单权限
func (s *PermissionService) AssignRoleMenus(roleID uint64, menuIDs []uint64) error {
	// 开启事务
	return s.DB.Transaction(func(tx *gorm.DB) error {
		// 1. 删除原有权限
		if err := tx.Where("role_id = ?", roleID).Delete(&model.SysRoleMenu{}).Error; err != nil {
			return err
		}

		// 2. 添加新权限
		if len(menuIDs) > 0 {
			roleMenus := make([]model.SysRoleMenu, 0, len(menuIDs))
			for _, menuID := range menuIDs {
				roleMenus = append(roleMenus, model.SysRoleMenu{
					RoleID: roleID,
					MenuID: menuID,
				})
			}

			if err := tx.CreateInBatches(roleMenus, 100).Error; err != nil {
				return err
			}
		}

		return nil
	})
}

// AssignUserMenus 为用户分配特殊菜单权限
func (s *PermissionService) AssignUserMenus(userID uint64, grantMenuIDs []uint64, denyMenuIDs []uint64) error {
	// 开启事务
	return s.DB.Transaction(func(tx *gorm.DB) error {
		// 1. 删除原有特殊权限
		if err := tx.Where("user_id = ?", userID).Delete(&model.SysUserMenu{}).Error; err != nil {
			return err
		}

		// 2. 添加授权权限
		if len(grantMenuIDs) > 0 {
			userMenus := make([]model.SysUserMenu, 0, len(grantMenuIDs))
			for _, menuID := range grantMenuIDs {
				userMenus = append(userMenus, model.SysUserMenu{
					UserID: userID,
					MenuID: menuID,
					Type:   "grant",
				})
			}

			if err := tx.CreateInBatches(userMenus, 100).Error; err != nil {
				return err
			}
		}

		// 3. 添加拒绝权限
		if len(denyMenuIDs) > 0 {
			userMenus := make([]model.SysUserMenu, 0, len(denyMenuIDs))
			for _, menuID := range denyMenuIDs {
				userMenus = append(userMenus, model.SysUserMenu{
					UserID: userID,
					MenuID: menuID,
					Type:   "deny",
				})
			}

			if err := tx.CreateInBatches(userMenus, 100).Error; err != nil {
				return err
			}
		}

		return nil
	})
}

// AssignTenantMenus 为租户分配可用菜单
func (s *PermissionService) AssignTenantMenus(tenantID uint64, menuIDs []uint64) error {
	// 开启事务
	return s.DB.Transaction(func(tx *gorm.DB) error {
		// 1. 删除原有分配
		if err := tx.Where("tenant_id = ?", tenantID).Delete(&model.SysTenantMenu{}).Error; err != nil {
			return err
		}

		// 2. 添加新分配
		if len(menuIDs) > 0 {
			tenantMenus := make([]model.SysTenantMenu, 0, len(menuIDs))
			for _, menuID := range menuIDs {
				tenantMenus = append(tenantMenus, model.SysTenantMenu{
					TenantID: tenantID,
					MenuID:   menuID,
				})
			}

			if err := tx.CreateInBatches(tenantMenus, 100).Error; err != nil {
				return err
			}
		}

		return nil
	})
}

// CheckPermission 检查用户是否有指定权限
func (s *PermissionService) CheckPermission(userID uint64, tenantID uint64, permission string) bool {
	// 1. 获取用户角色
	var roleIDs []uint64
	s.DB.Table("sys_user_role").
		Where("user_id = ?", userID).
		Pluck("role_id", &roleIDs)

	// 2. 检查是否是系统管理员
	if len(roleIDs) > 0 {
		var count int64
		s.DB.Model(&model.SysRole{}).
			Where("id IN ? AND code = ? AND tenant_id IS NULL", roleIDs, "admin").
			Count(&count)
		if count > 0 {
			return true // 系统管理员拥有所有权限
		}
	}

	// 3. 获取用户权限
	permissions, err := s.GetUserPermissions(userID, tenantID)
	if err != nil {
		zap.L().Error("获取用户权限失败", zap.Error(err))
		return false
	}

	// 4. 检查是否包含指定权限
	for _, p := range permissions {
		if p == permission {
			return true
		}
	}

	return false
}

// buildMenuTree 构建菜单树
func (s *PermissionService) buildMenuTree(menus []*model.SysMenu) []*model.SysMenu {
	// 创建一个映射表，用于快速查找菜单
	menuMap := make(map[uint64]*model.SysMenu)
	for _, menu := range menus {
		menuMap[uint64(menu.ID)] = menu
	}

	// 构建树形结构
	var rootMenus []*model.SysMenu
	for _, menu := range menus {
		if menu.ParentID == 0 {
			rootMenus = append(rootMenus, menu)
		} else {
			if parent, ok := menuMap[menu.ParentID]; ok {
				if parent.Children == nil {
					parent.Children = make([]*model.SysMenu, 0)
				}
				parent.Children = append(parent.Children, menu)
			}
		}
	}

	return rootMenus
}

// 辅助函数：取两个切片的交集
func intersection(a, b []uint64) []uint64 {
	set := make(map[uint64]bool)
	var result []uint64

	for _, item := range a {
		set[item] = true
	}

	for _, item := range b {
		if set[item] {
			result = append(result, item)
		}
	}

	return result
}
